/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-04-10     ZYH          first version
 * 2019-10-27     flybreak     Compatible with the HS
 */

#include <rtthread.h>
#include "board.h"

#ifdef BSP_USING_USBDEVICE

#include <rtdevice.h>
#include <string.h>
#include <drv_config.h>
#include "hw_config.h"
#include "usb_lib.h"
#include "usb_pwr.h"
#include "usb_istr.h"

static uint32_t _stm_pcd;
static struct udcd _stm_udc;
static struct ep_id _ep_pool[] =
{
    {0x0,  USB_EP_ATTR_CONTROL,     USB_DIR_INOUT,  64, ID_ASSIGNED  },
    {0x1,  USB_EP_ATTR_BULK,        USB_DIR_IN,     64, ID_UNASSIGNED},
    {0x1,  USB_EP_ATTR_BULK,        USB_DIR_OUT,    64, ID_UNASSIGNED},
    {0x2,  USB_EP_ATTR_INT,         USB_DIR_IN,     64, ID_UNASSIGNED},
    {0x2,  USB_EP_ATTR_INT,         USB_DIR_OUT,    64, ID_UNASSIGNED},
    {0x3,  USB_EP_ATTR_BULK,        USB_DIR_IN,     64, ID_UNASSIGNED},
#if !defined(SOC_SERIES_STM32F1)
    {0x3,  USB_EP_ATTR_BULK,        USB_DIR_OUT,    64, ID_UNASSIGNED},
#endif
    {0xFF, USB_EP_ATTR_TYPE_MASK,   USB_DIR_MASK,   0,  ID_ASSIGNED  },
};
uint32_t packet_sent=1;
uint32_t packet_receive=1;


void USBD_IRQ_HANDLER(void)
{
    rt_interrupt_enter();
    USB_Istr();
    /* leave interrupt */
    rt_interrupt_leave();
}
/**
 * @brief  This function handles USB WakeUp interrupt request.
 */
void USBWakeUp_IRQHandler(void)
{
    rt_interrupt_enter();
    EXTI_ClrITPendBit(EXTI_LINE18);
    rt_kprintf("USBWakeUp! tick=%d",rt_tick_get());
    /* leave interrupt */
    rt_interrupt_leave();
}

void HAL_PCD_ResetCallback(uint32_t *pcd)
{

    rt_usbd_reset_handler(&_stm_udc);
}

void HAL_PCD_SetupStageCallback(uint32_t *hpcd)
{

}

void HAL_PCD_DataInStageCallback(uint32_t *hpcd, uint8_t epnum)
{

}

void HAL_PCD_ConnectCallback(uint32_t *hpcd)
{
    rt_usbd_connect_handler(&_stm_udc);
}

void HAL_PCD_SOFCallback(uint32_t *hpcd)
{
    rt_usbd_sof_handler(&_stm_udc);
}

void HAL_PCD_DisconnectCallback(uint32_t *hpcd)
{
    rt_usbd_disconnect_handler(&_stm_udc);
}

void HAL_PCD_DataOutStageCallback(uint32_t *hpcd, uint8_t epnum)
{

}

void HAL_PCDEx_SetConnectionState(uint32_t *hpcd, uint8_t state)
{
    if (state == 1)
    {
#if defined(SOC_SERIES_STM32F1)
    rt_pin_mode(BSP_USB_CONNECT_PIN,PIN_MODE_OUTPUT);
    rt_pin_write(BSP_USB_CONNECT_PIN, BSP_USB_PULL_UP_STATUS);
#endif
    }
    else
    {
#if defined(SOC_SERIES_STM32F1)
    rt_pin_mode(BSP_USB_CONNECT_PIN,PIN_MODE_OUTPUT);
    rt_pin_write(BSP_USB_CONNECT_PIN, !BSP_USB_PULL_UP_STATUS);
#endif
    }
}

static rt_err_t _ep_set_stall(rt_uint8_t address)
{

    return RT_EOK;
}

static rt_err_t _ep_clear_stall(rt_uint8_t address)
{

    return RT_EOK;
}

static rt_err_t _set_address(rt_uint8_t address)
{

    return RT_EOK;
}

static rt_err_t _set_config(rt_uint8_t address)
{
    return RT_EOK;
}

static rt_err_t _ep_enable(uep_t ep)
{

    return RT_EOK;
}

static rt_err_t _ep_disable(uep_t ep)
{

    return RT_EOK;
}

static rt_size_t _ep_read(rt_uint8_t address, void *buffer)
{
    rt_size_t size = 0;
    RT_ASSERT(buffer != RT_NULL);
    /*rt_kprintf("_ep_read().tick=%d.",rt_tick_get());*/
    return size;
}

static rt_size_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size)
{
    /*rt_kprintf("_ep_read_prepare().tick=%d.",rt_tick_get());*/
    return size;
}

static rt_size_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size)
{
    /*rt_kprintf("_ep_write().size=%d.tick=%d.",size,rt_tick_get());*/
    /* send  packet to PMA*/
    USB_CopyUserToPMABuf((unsigned char*)buffer, ENDP1_TXADDR, size);
    USB_SetEpTxCnt(ENDP1, size);
    USB_SetEpTxValid(ENDP1);
    return size;
}

static rt_err_t _ep0_send_status(void)
{

    return RT_EOK;
}

static rt_err_t _suspend(void)
{
    return RT_EOK;
}

static rt_err_t _wakeup(void)
{
    return RT_EOK;
}

static rt_err_t _init(rt_device_t device)
{


    /* Initialize LL Driver */
    USB_Init();
    /* USB interrupt Init */
    USB_Interrupts_Config();

    return RT_EOK;
}

const static struct udcd_ops _udc_ops =
{
    _set_address,
    _set_config,
    _ep_set_stall,
    _ep_clear_stall,
    _ep_enable,
    _ep_disable,
    _ep_read_prepare,
    _ep_read,
    _ep_write,
    _ep0_send_status,
    _suspend,
    _wakeup,
};

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops _ops =
{
    _init,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
};
#endif

int stm_usbd_register(void)
{
    rt_memset((void *)&_stm_udc, 0, sizeof(struct udcd));
    _stm_udc.parent.type = RT_Device_Class_USBDevice;
#ifdef RT_USING_DEVICE_OPS
    _stm_udc.parent.ops = &_ops;
#else
    _stm_udc.parent.init = _init;
#endif
    _stm_udc.parent.user_data = &_stm_pcd;
    _stm_udc.ops = &_udc_ops;
    /* Register endpoint infomation */
    _stm_udc.ep_pool = _ep_pool;
    _stm_udc.ep0.id = &_ep_pool[0];
#ifdef BSP_USBD_SPEED_HS
    _stm_udc.device_is_hs = RT_TRUE;
#endif
    rt_device_register((rt_device_t)&_stm_udc, "usbd", 0);
    rt_usb_device_init();
    return RT_EOK;
}
INIT_DEVICE_EXPORT(stm_usbd_register);
#endif
